SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
yesterdaylast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
2.7 KB · 54 lines tsx
Raw Blame History
1"use client";23import type { CountryDetail } from "@dci/core";4import { AreaChart } from "@/components/ui/charts";5import { EmptyState } from "@/components/ui/states";6import { fmtInt, fmtMw } from "@/lib/format";78/**9 * Facilities over time (cumulative count by opening year) and, when published, cumulative known MW.10 * Rendered as two single-axis charts (never dual-axis): MW only when at least two years carry a figure.11 */12export function GrowthChart({ data }: { data: CountryDetail["growth"] }) {13  const rows = [...data].filter((d) => Number.isFinite(d.year)).sort((a, b) => a.year - b.year);14  if (rows.length < 2) return <EmptyState compact title="Not enough dated openings" description="Growth charts appear once at least two opening years are published for facilities in this country." />;15  // fill the years between the first and last observation so the curve is continuous16  const first = rows[0]!.year;17  const last = Math.max(rows[rows.length - 1]!.year, new Date().getUTCFullYear());18  const byYear = new Map(rows.map((r) => [r.year, r]));19  const facilities: Array<{ x: number; y: number }> = [];20  const mw: Array<{ x: number; y: number | null }> = [];21  let cumMw: number | null = null;22  let hasMwYears = 0;23  for (let y = first; y <= last; y++) {24    const r = byYear.get(y);25    // API rows are already cumulative facility counts by opening year26    const prevF = facilities.length ? facilities[facilities.length - 1]!.y : 0;27    facilities.push({ x: y, y: r ? Math.max(prevF, r.facilities) : prevF });28    if (r && r.knownMw !== null) {29      cumMw = (cumMw ?? 0) + r.knownMw;30      hasMwYears++;31    }32    mw.push({ x: y, y: cumMw });33  }34  const span = facilities.length;35  const step = span > 40 ? 5 : span > 20 ? 2 : 1;36  const thin = <T extends { x: number }>(pts: T[]) => pts.filter((p, i) => i === pts.length - 1 || (p.x - first) % step === 0);37  return (38    <div className="grid gap-6 md:grid-cols-2">39      <div>40        <div className="label-caps mb-2">Facilities (cumulative, by opening year)</div>41        <AreaChart series={[{ name: "Facilities", data: thin(facilities), color: "var(--ink)" }]} height={180} format={(v) => fmtInt(v)} ariaLabel="Cumulative facilities by opening year" />42      </div>43      <div>44        <div className="label-caps mb-2">Known MW (cumulative, published figures)</div>45        {hasMwYears >= 2 ? (46          <AreaChart series={[{ name: "Known MW", data: thin(mw), color: "var(--accent)" }]} height={180} format={(v) => fmtMw(v)} ariaLabel="Cumulative known capacity by opening year" />47        ) : (48          <EmptyState compact title="MW not published by year" description="Operators in this country have not published enough dated capacity figures to chart." />49        )}50      </div>51    </div>52  );53}54